#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <mutex>
#include "util.hpp"
#include "log.hpp"

namespace ns_index
{
    struct DocInfo
    {
        std::string title;//文档标题
        std::string content;//文档内容
        std::string url;//官方文档url
        uint64_t doc_id;//文档id
    };

    struct InvertedElem
    {
        uint64_t doc_id;
        std::string word;
        int weight;
        InvertedElem()
        :weight(0)
        {}
    };
    //倒排拉链
    typedef std::vector<InvertedElem> InvertedList;

    class Index
    {
    private:
        static Index* instance;//单例模式
        static std::mutex _mutex;//获取单例用锁
        //正排索引，文档id ->文档内容，数组的下标即id
        std::vector<DocInfo> forward_index;
        //倒排索引, 关键字 -> 一组文档id（即倒排拉链）
        std::unordered_map<std::string,InvertedList> inverted_index;
    public:
        static Index* GetInstance()
        {
            if(nullptr == instance)
            {
                _mutex.lock();
                if(nullptr == instance)
                {
                    instance = new Index;
                }
                _mutex.unlock();
            }
            return instance;
        }
        DocInfo* GetForwardIndex(uint64_t doc_id)
        {
            if(doc_id >= forward_index.size())
            {
                std::cerr<<"doc_id out of range,error!"<<std::endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }
        InvertedList* GetInvertedList(const std::string& word)
        {
            auto it = inverted_index.find(word);
            if(it == inverted_index.end())
            {
                std::cerr<<word<<"have no InvertedList"<<std::endl;
                return nullptr;
            }
            return &(it->second);
        }
        //更据parse处理后的数据，构建正排和倒排索引
        bool BuildIndex(const std::string& input)
        {
            std::ifstream in(input,std::ios::in | std::ios::binary);
            if(!in.is_open())
            {
                std::cerr<<"sorry, "<<input<<" open error"<<std::endl;
                return false;
            }
            std::string line;
            int count = 0;
            while(std::getline(in,line))
            {
                //给该文档建立正排索引
                DocInfo* doc = BuildForwardIndex(line);
                if(doc == nullptr)
                {
                    std::cerr<<"build "<<line<<" error"<<std::endl;
                    continue;
                }
                //建立倒排索引
                BuildInvertedIndex(*doc);
                count++;
                if(count%50==0)
                    //std::cout<<"已建立索引的文件: "<<count<<std::endl;
					LOG(NORMAL,"已建立索引的文件" + std::to_string(count));
            }
            return true;
        }
    private:
        Index(){};
        Index(const Index&) = delete;
        Index& operator=(const Index&) = delete;
    private:
        DocInfo* BuildForwardIndex(const std::string& line)
        {
            //1. 切分字符串
            std::vector<std::string> results;
            const std::string sep = "\3";
            ns_util::StringUtil::Spilit(line,&results,sep);
            if(results.size() != 3)
                return nullptr;

            //2. 将切分后的内容填入DocInfo中
            DocInfo doc;
            doc.title = results[0];
            doc.content = results[1];
            doc.url = results[2];
            doc.doc_id = forward_index.size();//插入前的索引数正好是序号

            forward_index.push_back(std::move(doc));
            return &forward_index.back();//返回新插入的
        }
        bool BuildInvertedIndex(const DocInfo& doc)
        {
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;
                word_cnt()
                :title_cnt(0),content_cnt(0)
                {}
            };
            std::unordered_map<std::string,word_cnt> word_map;//存储词频映射
            
            //标题分词
            std::vector<std::string> title_words;
            ns_util::JiebaUtil::CutString(doc.title,&title_words);
            //词频统计
            for(std::string s:title_words)
            {
                //转为小写再统计
                boost::to_lower(s);
                word_map[s].title_cnt++;
            }

            //内容分词
            std::vector<std::string> content_words;
            ns_util::JiebaUtil::CutString(doc.content,&content_words);
            //词频统计
            for(std::string s:content_words)
            {
                boost::to_lower(s);
                word_map[s].content_cnt++;
            }

            const int t_weight = 10;//标题词频权值
            const int c_weight = 1;//内容词频权值
            for(auto& word_pair:word_map)
            {
                InvertedElem item;
                item.doc_id = doc.doc_id;
                item.word = word_pair.first;
                item.weight = t_weight * word_pair.second.title_cnt + c_weight* word_pair.second.content_cnt;
                InvertedList& inverted_list = inverted_index[word_pair.first];
                inverted_list.push_back(std::move(item));
            }
            return true;
        }
    };
    Index* Index::instance = nullptr;
    std::mutex Index::_mutex;
}
